package org.finalministry.gui.tests;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.finalministry.utils.geo.GeoUtils;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLD;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/*
 * File:  geoMap.java
 * Author:  J. Barr
 * Last Modified:  6/2012
 */

/**
 * Purpose: objects of this class represent a map. The methods of this class
 * allow points, lines, and polygons to be added to the map. In addition, the
 * methods allow the features to be colored and allow attributes to be stored
 * with the features. The class can also be used to create buffers around
 * features. Each type of feature is added in a separate map layer.
 */

public class geoMap {

	// The variable "map" will represent the map being displayed
	MapContent map;
	// The variable "style" will represent the current style
	Style style;

	static StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
	static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();

	public static void main(String[] args) throws Exception {
		geoMap map = new geoMap("TEST");
	}

	public geoMap(String test) throws Exception {
		makeTestMap();
	}

	/**
	 * Purpose: construct a geoMap object
	 * 
	 * @param: none
	 * @return: none
	 *          <p>
	 *          Outputs: none
	 *          <p>
	 *          Properties modified: map and style are initialized to null
	 */
	public geoMap() {
		map = null;
		style = null;
	}

	/**
	 * Purpose: construct a geoMap object and initialize it with a style and
	 * layer
	 * 
	 * @param theTitle
	 *            the title to be displayed in the map window
	 * @param strokeColor
	 *            the color of the lines of the layer features
	 * @param fillColor
	 *            the color of the fill of the layer features
	 * @param lineWidth
	 *            the width of the lines of the layer features
	 * @return: none
	 *          <p>
	 *          Outputs: none
	 *          <p>
	 *          Properties modified: map and style
	 */
	public geoMap(String theTitle, Color strokeColor, Color fillColor, int lineWidth) throws Exception {
		makeMap(theTitle, strokeColor, fillColor, lineWidth);
	}

	/**
	 * Purpose: construct a map object and initialize it with a style and a
	 * single layer
	 * 
	 * @param theTitle
	 *            the title to be displayed in the map window
	 * @param strokeColor
	 *            the color of the lines of the layer features
	 * @param fillColor
	 *            the color of the fill of the layer features
	 * @param lineWidth
	 *            the width of the lines of the layer features
	 * @return: none
	 *          <p>
	 *          Outputs: none
	 *          <p>
	 *          Properties modified: map and style
	 */
	public void makeMap(String theTitle, Color strokeColor, Color fillColor, int lineWidth) throws Exception {
		// display a data store file chooser dialog for shapefiles
		System.out.println("Starting map...");
		File file = JFileDataStoreChooser.showOpenFile("shp", null);
		if (file == null) {
			return;
		}

		FileDataStore store = FileDataStoreFinder.getDataStore(file);
		SimpleFeatureSource featureSource = store.getFeatureSource();

		// Create a map content and add our shapefile to it
		map = new MapContent();
		map.setTitle(theTitle);

		if (strokeColor == null || fillColor == null || lineWidth < 0)
			style = SLD.createSimpleStyle(featureSource.getSchema());
		else
			style = createStyle(featureSource, strokeColor, fillColor, lineWidth);
		Layer layer = new FeatureLayer(featureSource, style);

		map.addLayer(layer);

		// Now display the map
		JMapFrame.showMap(map);
	}

	public void makeTestMap() throws Exception {

		SimpleFeatureTypeBuilder tbuild = new SimpleFeatureTypeBuilder();
		tbuild.setName("Location");
		tbuild.add("location", Point.class);
		SimpleFeatureType TYPE = tbuild.buildFeatureType();

		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
		List<SimpleFeature> features = new ArrayList<SimpleFeature>();

		String sielmingenPolygon = "POLYGON ((1029174.6324667 6219656.921194,1028994.8860174 6219620.4941727,1028842.6091251 6219561.3749087,1028615.6866974 6219453.2881734,1028469.9786123 6219336.8411381,1028425.7884553 6219286.6793383,1028370.8493412 6219188.1472315,1028254.402306 6218977.9454038,1028207.8234919 6218904.4941969,1028180.9510991 6218859.1097114,1028161.2446778 6218814.9195544,1028141.5382564 6218776.7010403,1028118.8460136 6218751.0229761,1028087.7934709 6218691.3065477,1028096.7509352 6218627.4099694,1028105.1112351 6218532.4608484,1028111.6800422 6218510.9629342,1028207.8234919 6218336.5909634,1028274.1087273 6218290.6093136,1028357.711727 6218255.3766209,1028440.7175624 6218121.0146571,1028495.6566765 6218029.6485217,1028610.3122189 6218026.6627003,1029196.1303809 6218602.9262338,1029320.3405518 6219228.7544028,1029306.008609 6219682.5992582,1029174.6324667 6219656.921194))";
		Geometry polygon = GeoUtils.getGeometryFromPolygon(sielmingenPolygon);
		featureBuilder.add(polygon);
		SimpleFeature feature = featureBuilder.buildFeature(null);
		features.add(feature);
		ListFeatureCollection collection = new ListFeatureCollection(TYPE);
		collection.add(feature);
		// Create a map content and add our shapefile to it
		map = new MapContent();
		map.setTitle("TEST");

		// StyleFactory sf = CommonFactoryFinder.getStyleFactory();
		style = SLD.createSimpleStyle(TYPE);
		// style = sf.createStyle();

		// style = createPolygonStyle(new Color(255, 0, 0), new Color(100, 100,
		// 100), 10);

		Layer layer = new FeatureLayer(collection, style);
		map.addLayer(layer);
		// Now display the map
		JMapFrame.showMap(map);

	}

	/**
	 * Purpose: construct a new layer for this map with a default style. The
	 * user is prompted for the shapefile to use in the layer.
	 * 
	 * @param: none
	 * @return: none
	 *          <p>
	 *          Outputs: none
	 *          <p>
	 *          Properties modified: map (layer is added) and style
	 */
	public void addLayer() throws Exception {
		File file = JFileDataStoreChooser.showOpenFile("shp", null);
		if (file == null) {
			return;
		}

		FileDataStore store = FileDataStoreFinder.getDataStore(file);
		SimpleFeatureSource featureSource = store.getFeatureSource();
		style = SLD.createSimpleStyle(featureSource.getSchema());
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);

		// Don't need to do this; will automatically update the map
		// JMapFrame.showMap(map);
	}

	/**
	 * Purpose: construct a new style and layer for this map. The user is
	 * prompted for the shapefile to use in the layer.
	 * 
	 * @param strokeColor
	 *            the color of the lines of the layer features
	 * @param fillColor
	 *            the color of the fill of the layer features
	 * @param lineWidth
	 *            the width of the lines of the layer features
	 * @return: none
	 *          <p>
	 *          Outputs: none
	 *          <p>
	 *          Properties modified: map and style
	 */
	public void addLayer(Color strokeColor, Color fillColor, int lineWidth) throws Exception {
		File file = JFileDataStoreChooser.showOpenFile("shp", null);
		if (file == null) {
			return;
		}

		FileDataStore store = FileDataStoreFinder.getDataStore(file);
		SimpleFeatureSource featureSource = store.getFeatureSource();

		if (strokeColor == null || fillColor == null || lineWidth < 0)
			style = SLD.createSimpleStyle(featureSource.getSchema());
		else
			style = createStyle(featureSource, strokeColor, fillColor, lineWidth);
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);

		// Don't need to do this; will automatically update the map
		JMapFrame.showMap(map);
	}

	/**
	 * <p>
	 * Purpose: This methods works out what sort of feature geometry we have in
	 * the shapefile and then delegates to an appropriate style creating method.
	 * 
	 * @param featureSource
	 *            a pointer to the features that will be modified
	 * @param stroke
	 *            the color of the lines of the layer features
	 * @param fillColor
	 *            the color of the fill of the layer features
	 * @param lineWidth
	 *            the width of the lines of the layer features
	 *            <p>
	 *            Outputs: none
	 *            <p>
	 *            Properties modified: map and style
	 **/
	private Style createStyle(FeatureSource featureSource, Color stroke, Color fillColor, int lineWidth) {
		SimpleFeatureType schema = (SimpleFeatureType) featureSource.getSchema();
		Class geomType = schema.getGeometryDescriptor().getType().getBinding();

		if (Polygon.class.isAssignableFrom(geomType) || MultiPolygon.class.isAssignableFrom(geomType)) {
			return createPolygonStyle(stroke, fillColor, lineWidth);

		} else if (LineString.class.isAssignableFrom(geomType) || MultiLineString.class.isAssignableFrom(geomType)) {
			return createLineStyle(stroke, lineWidth);

		} else {
			return createPointStyle(stroke, fillColor, lineWidth);
		}
	}

	/**
	 * Create a Style to draw polygon features with a thin blue outline and a
	 * cyan fill
	 */
	private Style createPolygonStyle(Color strokeColor, Color fillColor, int lineWidth) {

		// create a partially opaque outline stroke
		Stroke stroke = styleFactory.createStroke(
		// filterFactory.literal(Color.BLUE),
				filterFactory.literal(strokeColor),
				// filterFactory.literal(1),
				filterFactory.literal(lineWidth), filterFactory.literal(0.5));

		// create a partial opaque fill
		Fill fill = styleFactory.createFill(filterFactory.literal(fillColor), filterFactory.literal(0.5));

		/*
		 * Setting the geometryPropertyName arg to null signals that we want to
		 * draw the default geomettry of features
		 */
		PolygonSymbolizer sym = styleFactory.createPolygonSymbolizer(stroke, fill, null);

		Rule rule = styleFactory.createRule();
		rule.symbolizers().add(sym);
		FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
		style = styleFactory.createStyle();
		style.featureTypeStyles().add(fts);

		return style;
	}

	/**
	 * Create a Style to draw line features as thin blue lines
	 */
	private Style createLineStyle(Color strokeColor, int lineWidth) {
		Stroke stroke = styleFactory.createStroke(filterFactory.literal(Color.BLUE), filterFactory.literal(1));

		/*
		 * Setting the geometryPropertyName arg to null signals that we want to
		 * draw the default geomettry of features
		 */
		LineSymbolizer sym = styleFactory.createLineSymbolizer(stroke, null);

		Rule rule = styleFactory.createRule();
		rule.symbolizers().add(sym);
		FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
		style = styleFactory.createStyle();
		style.featureTypeStyles().add(fts);

		return style;
	}

	/**
	 * Create a Style to draw point features as circles with blue outlines and
	 * cyan fill
	 */
	private Style createPointStyle(Color strokeColor, Color fillColor, int lineWidth) {
		Graphic gr = styleFactory.createDefaultGraphic();

		Mark mark = styleFactory.getCircleMark();

		mark.setStroke(styleFactory.createStroke(
		// filterFactory.literal(Color.BLUE), filterFactory.literal(1)));
				filterFactory.literal(strokeColor), filterFactory.literal(lineWidth)));

		// mark.setFill(styleFactory.createFill(filterFactory.literal(Color.CYAN)));
		mark.setFill(styleFactory.createFill(filterFactory.literal(fillColor)));

		gr.graphicalSymbols().clear();
		gr.graphicalSymbols().add(mark);
		gr.setSize(filterFactory.literal(5));

		/*
		 * Setting the geometryPropertyName arg to null signals that we want to
		 * draw the default geomettry of features
		 */
		PointSymbolizer sym = styleFactory.createPointSymbolizer(gr, null);

		Rule rule = styleFactory.createRule();
		rule.symbolizers().add(sym);
		FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
		style = styleFactory.createStyle();
		style.featureTypeStyles().add(fts);

		return style;
	}

	public void addPointLayer(String layerName, double[] lats, double[] longs, String[] names, double[] values, Color stroke, Color fillColor,
			int lineWidth) throws Exception {

		/*
		 * We use the DataUtilities class to create a FeatureType that will
		 * describe the data in our shapefile.
		 * 
		 * See also the createFeatureType method below for another, more
		 * flexible approach.
		 */
		final SimpleFeatureType TYPE = DataUtilities.createType("Location",
		// "location:Point:srid=4326," + // <- the geometry attribute: Point
		// type
				"location:Point:srid=4326," + "name:String," + // <- a String
																// attribute
						"number:Double" // a number attribute
		);

		/*
		 * We create a FeatureCollection into which we will put each Feature
		 * created from a record in the input csv data file
		 */
		DefaultFeatureCollection collection = new DefaultFeatureCollection();
		/*
		 * GeometryFactory will be used to create the geometry attribute of each
		 * feature (a Point object for the location)
		 */
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		int numFeatures = lats.length;

		System.out.println("Starting the point layer...");

		for (int i = 0; i < numFeatures; i++) {

			double latitude = lats[i];
			double longitude = longs[i];
			String name = names[i];
			double number = values[i];

			/* Longitude (= x coord) first ! */
			Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));

			// featureBuilder.add(point);
			featureBuilder.add(point);
			// this creates a polygon of the given buffer size; use the number
			// field for the buffer size
			featureBuilder.add(name);
			featureBuilder.add(number);
			SimpleFeature feature = featureBuilder.buildFeature(null);
			if (feature == null)
				System.out.println("Null feature!");
			collection.add(feature);
			System.out.println("added point feature to collection at latitude:  " + latitude + " and longitude:  " + longitude);
		}
		/*
		 * Get an output file name and create the new shapefile
		 */
		File file = new File(layerName);
		File newFile = getNewShapeFile(file);

		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", newFile.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		newDataStore.createSchema(TYPE);

		/*
		 * You can comment out this line if you are using the createFeatureType
		 * method (at end of class file) rather than DataUtilities.createType
		 */
		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		/*
		 * Write the features to the shapefile
		 */
		Transaction transaction = new DefaultTransaction("create");

		String typeName = newDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();

			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();

			} finally {
				transaction.close();
			}
			System.out.println("successfully added data to the database");
			// System.exit(0); // success!
		} else {
			System.out.println(typeName + " does not support read/write access");
			System.exit(1);
		}

		System.out.println("saved the new point layer shapefile");
		// now add the new shapefile to the map
		FileDataStore store = FileDataStoreFinder.getDataStore(newFile);
		featureSource = store.getFeatureSource();
		// if there was a stroke color passed in, create a style
		if (stroke != null && fillColor != null && lineWidth > 0) {
			style = createStyle(featureSource, stroke, fillColor, lineWidth);
		} else
			style = SLD.createSimpleStyle(featureSource.getSchema());
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);
		System.out.println("...added the point layer to the map\n");

		// Don't need to do this; will automatically update the map
		// JMapFrame.showMap(map);
	} // end addPointLayer

	public void addBufferLayer(String layerName, double[] lats, double[] longs, String[] names, double[] values, Color stroke, Color fillColor,
			int lineWidth) throws Exception {

		/*
		 * We use the DataUtilities class to create a FeatureType that will
		 * describe the data in our shapefile.
		 * 
		 * See also the createFeatureType method below for another, more
		 * flexible approach.
		 */
		final SimpleFeatureType TYPE = DataUtilities.createType("Location",
		// "location:Point:srid=4326," + // <- the geometry attribute: Point
		// type
				"location:Polygon:srid=4326," + "name:String," + // <- a String
																	// attribute
						"number:Double" // a number attribute
		);

		/*
		 * We create a FeatureCollection into which we will put each Feature
		 * created from a record in the input csv data file
		 */
		DefaultFeatureCollection collection = new DefaultFeatureCollection();
		/*
		 * GeometryFactory will be used to create the geometry attribute of each
		 * feature (a Point object for the location)
		 */
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		System.out.println("Starting the buffer layer...");

		int numFeatures = lats.length;

		for (int i = 0; i < numFeatures; i++) {

			double latitude = lats[i];
			double longitude = longs[i];
			String name = names[i];
			double number = values[i];

			/* Longitude (= x coord) first ! */
			Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));

			// put a buffer around the point
			System.out.println("buffer distance = " + number);
			Coordinate[] coords = point.buffer(number).getCoordinates();
			LinearRing LR = geometryFactory.createLinearRing(coords);
			// this creates a polygon of the given buffer size; use the number
			// field for the buffer size
			Polygon poly = geometryFactory.createPolygon(LR, null);

			featureBuilder.add(poly);
			featureBuilder.add(name);
			featureBuilder.add(number);
			SimpleFeature feature = featureBuilder.buildFeature(null);
			if (feature == null)
				System.out.println("Null feature!");
			collection.add(feature);
			System.out.println("added buffer feature to collection at latitude:  " + latitude + " and longitude:  " + longitude);
		}
		/*
		 * Get an output file name and create the new shapefile
		 */
		File file = new File(layerName);
		File newFile = getNewShapeFile(file);

		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", newFile.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		newDataStore.createSchema(TYPE);

		/*
		 * You can comment out this line if you are using the createFeatureType
		 * method (at end of class file) rather than DataUtilities.createType
		 */
		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		/*
		 * Write the features to the shapefile
		 */
		Transaction transaction = new DefaultTransaction("create");

		String typeName = newDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();

			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();

			} finally {
				transaction.close();
			}
			System.out.println("successfully added data to the database");
			// System.exit(0); // success!
		} else {
			System.out.println(typeName + " does not support read/write access");
			System.exit(1);
		}

		System.out.println("saved the new shapefile");
		// now add the new shapefile to the map
		FileDataStore store = FileDataStoreFinder.getDataStore(newFile);
		featureSource = store.getFeatureSource();
		// if there was a stroke color passed in, create a style
		if (stroke != null && fillColor != null && lineWidth > 0) {
			style = createStyle(featureSource, stroke, fillColor, lineWidth);
		} else
			style = SLD.createSimpleStyle(featureSource.getSchema());
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);
		System.out.println("..added the buffer layer to the map\n");

		// Don't need to do this; will automatically update the map
		// JMapFrame.showMap(map);
	} // end addBufferLayer

	public void addPointCSV(Color stroke, Color fillColor, int lineWidth) throws Exception {

		// Open the CSV file
		File file = JFileDataStoreChooser.showOpenFile("csv", null);
		if (file == null) {
			return;
		}

		/*
		 * We use the DataUtilities class to create a FeatureType that will
		 * describe the data in our shapefile.
		 * 
		 * See also the createFeatureType method below for another, more
		 * flexible approach.
		 */
		final SimpleFeatureType TYPE = DataUtilities.createType("Location",
		// "location:Point:srid=4326," + // <- the geometry attribute: Point
		// type
				"location:Polygon:srid=4326," + "name:String," + // <- a String
																	// attribute
						"number:Integer" // a number attribute
		);

		/*
		 * We create a FeatureCollection into which we will put each Feature
		 * created from a record in the input csv data file
		 */
		DefaultFeatureCollection collection = new DefaultFeatureCollection();
		/*
		 * GeometryFactory will be used to create the geometry attribute of each
		 * feature (a Point object for the location)
		 */
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		BufferedReader reader = new BufferedReader(new FileReader(file));

		System.out.println("Starting the CSV layer...");

		try {
			/* First line of the data file is the header */
			String line = reader.readLine();
			System.out.println("Header: " + line);
			double latitude;
			double longitude;

			for (line = reader.readLine(); line != null; line = reader.readLine()) {
				if (line.trim().length() > 0) { // skip blank lines
					String tokens[] = line.split("\\,");

					latitude = Double.parseDouble(tokens[0]);
					longitude = Double.parseDouble(tokens[1]);
					String name = tokens[2].trim();
					int number = Integer.parseInt(tokens[3].trim());

					/* Longitude (= x coord) first ! */
					Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));

					// put a buffer around the point
					double dist = number / 10.0;
					System.out.println("buffer distance = " + dist);
					Coordinate[] coords = point.buffer(dist).getCoordinates();
					LinearRing LR = geometryFactory.createLinearRing(coords);
					Polygon poly = geometryFactory.createPolygon(LR, null);

					// featureBuilder.add(point);
					featureBuilder.add(poly);
					// this should create a polygon of the given buffer size; I
					// think
					featureBuilder.add(name);
					featureBuilder.add(number);
					SimpleFeature feature = featureBuilder.buildFeature(null);
					if (feature == null)
						System.out.println("Null feature!");
					collection.add(feature);
					System.out.println("added CSV point feature to collection at latitude:  " + latitude + " and longitude:  " + longitude);
				}
			}
		} finally {
			reader.close();
		}

		/*
		 * Get an output file name and create the new shapefile
		 */
		File newFile = getNewShapeFile(file);

		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", newFile.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		newDataStore.createSchema(TYPE);

		/*
		 * You can comment out this line if you are using the createFeatureType
		 * method (at end of class file) rather than DataUtilities.createType
		 */
		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		/*
		 * Write the features to the shapefile
		 */
		Transaction transaction = new DefaultTransaction("create");

		String typeName = newDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();

			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();

			} finally {
				transaction.close();
			}
			System.out.println("successfully added data to the database");
			// System.exit(0); // success!
		} else {
			System.out.println(typeName + " does not support read/write access");
			System.exit(1);
		}

		System.out.println("stored the new shapefile");
		// now add the new shapefile to the map
		FileDataStore store = FileDataStoreFinder.getDataStore(newFile);
		featureSource = store.getFeatureSource();
		// if there was a stroke color passed in, create a style
		if (stroke != null) {
			style = createStyle(featureSource, stroke, fillColor, lineWidth);
		} else
			style = SLD.createSimpleStyle(featureSource.getSchema());
		Layer layer = new FeatureLayer(featureSource, style);
		map.addLayer(layer);
		System.out.println("...added the CSV point file to the map\n");

		// Don't need to do this; will automatically update the map
		// JMapFrame.showMap(map);
	} // end addPointCSV

	public void addLineLayer(String layerName, double[] lats, double[] longs, String[] names, double[] values, Color stroke, Color fillColor,
			int lineWidth) throws Exception {

		/*
		 * We use the DataUtilities class to create a FeatureType that will
		 * describe the data in our shapefile.
		 * 
		 * See also the createFeatureType method below for another, more
		 * flexible approach.
		 */
		final SimpleFeatureType TYPE = DataUtilities.createType("Location",
		// "location:Point:srid=4326," + // <- the geometry attribute: Point
		// type
				"location:LineString:srid=4326," + "name:String," + // <- a
																	// String
																	// attribute
						"number:Double" // a number attribute
		);

		/*
		 * We create a FeatureCollection into which we will put each Feature
		 * created from a record in the input csv data file
		 */
		DefaultFeatureCollection collection = new DefaultFeatureCollection();
		/*
		 * GeometryFactory will be used to create the geometry attribute of each
		 * feature (a Point object for the location)
		 */
		GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();

		// PositionFactory pFactory = new PositionFactory();

		SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

		int numFeatures = lats.length;

		System.out.println("Starting the Line layer...");

		// Coordinate[] lineCoords = new Coordinate[lats.length];
		Coordinate[] lineCoords = new Coordinate[numFeatures];
		String name = names[0];
		double number = values[0];

		for (int i = 0; i < numFeatures; i++) {

			lineCoords[i] = new Coordinate(longs[i], lats[i]);
			// lineCoords[1] = new Coordinate(longs[i+1], lats[i+1]);
			/* Longitude (= x coord) first ! */
		}
		// now add the line feature
		if (lineCoords[0] == null)
			System.out.println("no coordinates!");
		LineString line1 = geometryFactory.createLineString(lineCoords);
		if (line1 == null)
			System.out.println("line not created");
		featureBuilder.add(line1);
		featureBuilder.add(names[0]);
		featureBuilder.add(values[0]);
		SimpleFeature feature = featureBuilder.buildFeature(null);
		if (feature == null)
			System.out.println("Null feature!");
		collection.add(feature);
		System.out.println("added line segment feature to collection at latitude:  " + lineCoords[0].x + " and longitude:  " + lineCoords[0].y);

		/*
		 * Get an output file name and create the new shapefile
		 */
		File file = new File(layerName);
		File newFile = getNewShapeFile(file);

		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put("url", newFile.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);

		ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
		newDataStore.createSchema(TYPE);

		/*
		 * You can comment out this line if you are using the createFeatureType
		 * method (at end of class file) rather than DataUtilities.createType
		 */
		newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

		/*
		 * Write the features to the shapefile
		 */
		Transaction transaction = new DefaultTransaction("create");

		String typeName = newDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();

			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();

			} finally {
				transaction.close();
			}
			System.out.println("successfully added data to the database");
			// System.exit(0); // success!
		} else {
			System.out.println(typeName + " does not support read/write access");
			System.exit(1);
		}

		// now add the new shapefile to the map
		FileDataStore store = FileDataStoreFinder.getDataStore(newFile);
		featureSource = store.getFeatureSource();
		// if there was a stroke color passed in, create a style
		if (stroke != null && fillColor != null && lineWidth > 0) {
			style = createStyle(featureSource, stroke, fillColor, lineWidth);
		} else
			style = SLD.createSimpleStyle(featureSource.getSchema());
		Layer layer = new FeatureLayer(featureSource, style);
		if (layer == null)
			System.out.println("layer was null");
		map.addLayer(layer);
		System.out.println("map now has " + map.layers().size() + " layers");
		System.out.println("...added the line layer to the map\n");

		// Don't need to do this; will automatically update the map
		// JMapFrame.showMap(map);
	} // end addLineLayer

	/**
	 * Prompt the user for the name and path to use for the output shapefile
	 * 
	 * @param csvFile
	 *            the input csv file used to create a default shapefile name
	 * 
	 * @return name and path for the shapefile as a new File object
	 */
	private static File getNewShapeFile(File csvFile) {
		String path = csvFile.getAbsolutePath();
		String newPath = path.substring(0, path.length() - 4) + ".shp";

		JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
		chooser.setDialogTitle("Save shapefile");
		chooser.setSelectedFile(new File(newPath));

		int returnVal = chooser.showSaveDialog(null);

		if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
			// the user cancelled the dialog
			System.exit(0);
		}

		File newFile = chooser.getSelectedFile();
		if (newFile.equals(csvFile)) {
			System.out.println("Error: cannot replace " + csvFile);
			System.exit(0);
		}

		return newFile;
	}

}
